package exp11;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

public class AIPlayer {

    int level = 1;//默认随机下棋
    static int BOARD_SIZE = 15;//默认15，外部可传递值使之变化
    private static final Map<String, Integer> SCORE = new LinkedHashMap<>();//哈希表，便于查找
    int[][] boardStatus;

    private enum ChessModel {//棋盘模型
        LIAN_WU(10000000, new String[]{"11111"}), // 连五
        HUO_SI(1000000, new String[]{"011110"}),// 活四
        HUO_SAN(10000, new String[]{"001110", "011100", "010110", "011010"}),// 活三
        CHONG_SI(9000, new String[]{"11110", "01111", "10111", "11011", "11101"}),//冲四
        HUO_ER(100, new String[]{"001100", "011000", "000110", "001010", "010100"}),// 活二
        HUO_YI(80, new String[]{"010200", "002010", "020100", "001020", "201000", "000102", "000201"}),// 活一
        MIAN_SAN(30, new String[]{"001112", "010112", "011012", "211100", "211010"}), // 眠三
        MIAN_ER(10, new String[]{"011200", "001120", "002110", "021100", "110000", "000011", "000112", "211000"}),// 眠二
        MIAN_YI(1, new String[]{"001200", "002100", "000210", "000120", "210000", "000012"});  // 眠一
        final int score;//分数
        final String[] values;//局势数组

        ChessModel(int score, String[] values) {
            this.score = score;
            this.values = values;
        }

        @Override
        public String toString() {
            return Arrays.toString(this.values);
        }
    }

    static {
        // 初始化棋型分数表，便于访存数据
        for (ChessModel chessScore : ChessModel.values()) {
            for (String value : chessScore.values) {
                SCORE.put(value, chessScore.score);
            }
        }
    }

    public AIPlayer(int level, int BOARD_SIZE) {
        this.level = level;
        AIPlayer.BOARD_SIZE = BOARD_SIZE;
    }


    public int[] getPosition(int[][] boardStatus, int[] humanPosition) {
//        System.out.println("Last round human position is" + humanPosition[0] + "," + humanPosition[1]);
        //获取位置信息
        this.boardStatus = boardStatus;
        return switch (this.level) {
            case 1 -> randomPosition();
            case 2 -> lowerLevelPosition(humanPosition);
            case 3 -> highLevelPosition(humanPosition);
            default -> randomPosition();
        };
    }


    private int[] lowerLevelPosition(int[] humanPosition) {
        int score = -10000000;
        int[] bestPos = {0, 0};
        if (humanPosition[0] == -1 && humanPosition[1] == -1) {//如果是第一步下棋
            bestPos[0] = BOARD_SIZE / 2;
            bestPos[1] = BOARD_SIZE / 2;
            return bestPos;
        }
        /* 遍历所有能下棋的点位，评估各个点位的分值，选择分值最大的点位 */
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                if (this.boardStatus[i][j] != 0) {
                    // 该点已有棋子，跳过
                    continue;
                }
                // 评估该点AI得分
                int val = lowerEvaluate(new int[]{i, j});
                // 选择得分最高的点位
                if (val > score) {
                    // 最高分被刷新
                    score = val;
                    // 更新最佳点位
                    bestPos[0] = i;
                    bestPos[1] = j;
                }
            }
        }
        return bestPos;
    }

    //EXP11 Start
    private int lowerEvaluate(int[] point) {
        //Exp11 此处进行改进优化
        int score = 0;
        int huoSanNum = 0;
        int chongSiNum = 0;
        int huoErNum = 0;

        for (int i = 1; i < 5; i++) {
            String situation = getSituation(point, i);
            if (checkSituation(situation, ChessModel.HUO_SAN)) {
                huoSanNum++;
            } else if (checkSituation(situation, ChessModel.CHONG_SI)) {
                chongSiNum++;
            } else if (checkSituation(situation, ChessModel.HUO_ER)) {
                huoErNum++;
            }
            score += getScore(situation);
        }
        if (huoSanNum > 0 && huoErNum > 0) {//活三和活二的分数加成
            score *= 2;
        }
        if (chongSiNum > 0 && huoErNum > 0) {//冲四和活二的分数加成
            score *= 4;
        }
        if (huoSanNum > 1) {//活三的分数加成
            score *= 6;
        }
        if (chongSiNum > 0 && huoSanNum > 0) {//活三和冲四的分数加成
            score *= 8;
        }
        return score;
    }

    private int getScore(String situation) {
        //依据当前的棋盘返回分数
        for (String key : SCORE.keySet()) {
            if (situation.contains(key)) {
                return SCORE.get(key);
            }
        }
        return 0;
    }

    private String getSituation(int[] point, int direction) {
        //根据落点位置来确认棋盘局势，大方向 1.横 2.纵 3.左斜 4.右斜，实际上就是将中心点视作AI下棋位置，然后将四个大方向的棋子分别加入到stringBuffer中
        direction = direction * 2 - 1;
        StringBuffer stringBuffer = new StringBuffer();
        appendChess(stringBuffer, point, direction, 4);
        appendChess(stringBuffer, point, direction, 3);
        appendChess(stringBuffer, point, direction, 2);
        appendChess(stringBuffer, point, direction, 1);
        stringBuffer.append(1);//当前棋子标为AI
        appendChess(stringBuffer, point, direction + 1, 1);
        appendChess(stringBuffer, point, direction + 1, 2);
        appendChess(stringBuffer, point, direction + 1, 3);
        appendChess(stringBuffer, point, direction + 1, 4);
        return stringBuffer.toString();
    }

    private boolean checkSituation(String situation, ChessModel chessModel) {
        //判定是否有其他类型的棋子
        for (String value : chessModel.values) {
            if (situation.contains(value))
                return true;
        }
        return false;
    }

    private void appendChess(StringBuffer sb, int[] point, int direction, int offset) {
        //依据方向加棋子
        int chess = relativePoint(point, direction, offset);
        if (chess > -1) {
            if (this.boardStatus[point[0]][point[1]] == 2) { //便于后续拓展，当评估人类棋手时。
                if (chess > 0) {
                    chess = 3 - chess;
                }
            }
            sb.append(chess);
        }
    }

    private int relativePoint(int[] point, int direction, int offset) {
        //point代表当前棋子位置，direction是八个不同方向,offset表示偏移量
        int x = point[0], y = point[1];
        switch (direction) {
            case 1:
                x -= offset;
                break;
            case 2:
                x += offset;
                break;
            case 3:
                y -= offset;
                break;
            case 4:
                y += offset;
                break;
            case 5:
                x += offset;
                y -= offset;
                break;
            case 6:
                x -= offset;
                y += offset;
                break;
            case 7:
                x -= offset;
                y -= offset;
                break;
            case 8:
                x += offset;
                y += offset;
                break;
        }

        if (x < 0 || y < 0 || x >= BOARD_SIZE || y >= BOARD_SIZE) {
            // 越界
            return -1;
        }

        // 返回该位置的棋子值
        return this.boardStatus[x][y];
    }
    //EXP11 End

    private int[] highLevelPosition(int[] humanPosition) {
        //TODO exp12 此处进行改进优化
        int x = (int) ((BOARD_SIZE - 1) * Math.random());
        int y = (int) ((BOARD_SIZE - 1) * Math.random());
        return new int[]{x, y};
    }

    private int[] randomPosition() {//无脑下棋
        int x = (int) ((BOARD_SIZE - 1) * Math.random());
        int y = (int) ((BOARD_SIZE - 1) * Math.random());
        return new int[]{x, y};
    }

}



